package ar.edu.itba.it.poo.dd.frontend

import scala.collection.mutable.HashMap
import java.awt.Font
import ar.edu.itba.it.poo.dd.backend._
import ar.edu.itba.it.poo.dd.frontend.gui._
import ar.edu.itba.it.poo.dd.frontend.gui.ImageUtils._
import java.awt.{ Color, Dimension }
import java.awt.Image
import scala.swing.event.{ Key, KeyPressed }
import scala.swing._
import javax.swing.JOptionPane
import java.io.File
import javax.swing.JPanel
import scala.collection.immutable.TreeMap
import scala.collection.immutable.SortedMap

object GameFrame extends SimpleGUIApplication {
  val CELL_SIZE = 30

  def top = new MainFrame {
    title = "Desktop Dungeon"
    preferredSize = new Dimension(300, 300)
    val boards = createBoards(new File("src/main/resources/boards"));
    var board: Option[Board] = None

    private object SwingBoardListener extends BoardListener() {
      def bossDefeated = {
        JOptionPane.showMessageDialog(null, "YOU WIN", "Desktop Dungeons", 0)
        board = None
        updateUI
      }

      def playerKilled = {
        JOptionPane.showMessageDialog(null, "GAMEOVER", "Desktop Dungeons", 0)
        board = None
        updateUI
      }
    }

    menuBar = new MenuBar() {
      contents += new Menu("Juego") {
        contents += new Menu("Nuevo") {
          boards.keys.foreach { name =>
            contents += new MenuItem(new Action(name) {
              def apply() {
                board = Some(parse(boards(name)))
                updateUI
              }
            })
          }
        }
        contents += new MenuItem(new Action("Reiniciar") {
          def apply() {
            if (board.isDefined) {
              board = Some(parse(boards(board.get.name)))
              updateUI
            }
          }
        })
      }
      contents += new MenuItem(new Action("Salir") {
        def apply() { exit }
      })
    }

    private def createBoards(dir: File): Map[String, File] = {
      val boards = for {
        file <- dir.listFiles
        if file.getName().endsWith(".board")
      } yield (parse(file).name, file)

      SortedMap.empty[String, File] ++ boards
    }

    private def parse(file: File): Board = {
      try {
        Parser(SwingBoardListener).parse(file)
      } catch {
        case ParseException(message) => {
          JOptionPane.showMessageDialog(null, "Error al cargar mapa " + file.getName, "Desktop Dungeons", 0)
          exit
        }
      }
    }

    private def updateUI {
      if (board.isDefined) {
        val selectedBoard = board.get
        val container = new Container(selectedBoard)
        contents = container
        container.requestFocus
        preferredSize = new Dimension(selectedBoard.columns * 30 + 200, selectedBoard.rows * 30 + 50)
        size = new Dimension(selectedBoard.columns * 30 + 200, selectedBoard.rows * 30 + 50)
        repaint
      } else {
        contents = new BorderPanel
      }
    }

    private class Container(board: Board) extends BorderPanel {
      val gamePanel = new GamePanel(board.rows, board.columns, 30, new GamePanelListener() { def onMouseMoved(row: Int, column: Int) = onMove(row, column) }, Color.BLACK)
      var selectedEnemy: Option[Enemy] = None

      listenTo(keys)
      reactions += {
        case KeyPressed(_, key, _, _) =>
          key match {
            case Key.Left => board.movePlayer(Left)
            case Key.Right => board.movePlayer(Right)
            case Key.Up => board.movePlayer(Up)
            case Key.Down => board.movePlayer(Down)
            case _ =>
          }
          repaint
      }
      peer.add(gamePanel)

      override def paint(g: Graphics2D) {
        super.paint(g);
        def printMessage(message: String, y: Int) {
          g.setColor(Color.BLACK);
          var current = y;
          val lines = message.split("\n").toList;
          val h = lines.head
          g.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 12))
          g.drawString(lines.head, board.columns * CELL_SIZE + 15, current)
          current += g.getFontMetrics().getHeight()
          g.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 12))
          for (line <- lines.tail) {
            g.drawString(line, board.columns * CELL_SIZE + 15F, current)
            current += g.getFontMetrics().getHeight()
          }
        }
        val b = board

        for (i <- 0 to board.rows - 1; j <- 0 to board.columns - 1) {
          gamePanel.clear(i, j)
          val cell = board(i)(j)
          val image = Images.forTile(cell)
          if (image.isDefined) gamePanel.put(image.get, i, j)
        }

        g.clearRect(board.columns * 30, 0, 200, g.getClipBounds().getHeight().toInt)
        printMessage(playerInfo, 15)
        printMessage(enemyInfo(enemyToShow), 90)

        gamePanel.put(drawString(overlap(gamePanel.at(board.player.row, board.player.column), Images.player), board.player.level.toString, Color.WHITE), board.player.row, board.player.column)
        gamePanel.repaint()
      }

      private def enemyToShow = if (selectedEnemy.isDefined) Some(selectedEnemy.get) else neighborEnemy
      private def playerInfo = board.player.name + "\n" + fighterInfo(Some(board.player)).getOrElse("")
      private def enemyInfo(enemy: Option[Enemy]) = enemy.map { e: Enemy => e.name + "\n" }.getOrElse("") + fighterInfo(enemy).getOrElse("")
      private def fighterInfo(fighter: Option[Fighter]) = fighter map { f =>
        "Health: " + f.health + "/" + f.maxHealth + "\nStrength: " + f.strength + "\nLevel: " + f.level
      }

      private def neighborEnemy = board.enemiesInRange match {
        case (x :: xs) => Some(x)
        case Nil => None
      }

      private def onMove(row: Int, column: Int) {
        board(row)(column) match {
          case e: Enemy if e.visible && e.isAlive => selectedEnemy = Some(e)
          case _ => selectedEnemy = None
        }
        repaint
      }
    }
  }
}